const fastFlowService = {
  /**
   * 遍历树，当查找到id时进行对应的action操作
   * @param id 需要操作的节点id
   * @param tree 树
   * @param action 动作
   * @param source 是否返回树
   * @return {*} 如果source为true，则返回修改后的树的拷贝，否则返回action内进行操作的target值
   */
  actionById: (id, tree, action, source) => {
    let target = fastFlowService.doAction(id, tree, action, source);
    delete target.found;
    return target;
  },
  /**
   * 上面方法的子方法，本方法会产生found这个多余参数
   */
  doAction(id, tree, action, source) {
    const { leaves = [] } = tree;
    let target = null;
    let found = false;
    leaves.forEach((leaf, leafIndex) => {
      if(!found) {
        leaf.children.forEach((child, childIndex) => {
          if(!found) {
            if(child.leaves && child.leaves.length > 0) {
              target = fastFlowService.actionById(id, child, action, source);
              if(source && target) {
                found = target.found;
                delete target.found;
                leaf.children.splice(childIndex, 1, target);
              }
            } else if(child.id === id) {
              //如果source为true，则返回树；否则返回target
              found = true;
              switch(action.name) {
                case 'get_tree':
                  target = leaf;
                  break;
                case 'add_child':
                  leaf.children.splice(childIndex + 1, 0, {
                    id: action.id
                  });
                  break;
                case 'remove_child':
                  leaf.children.splice(childIndex, 1);
                  break;
                case 'add_tree':
                  const { ids = [] } = action;
                  let endNode = leaf.children.find(leafChild => leafChild.type === 'end');
                  let otherChildren = leaf.children.slice(childIndex + 1);
                  if(endNode)
                    otherChildren.pop();
                  let newMaxSpan = 1;
                  if(otherChildren.length > 0) {
                    otherChildren.map(c => {
                      if(c.span)
                        newMaxSpan = Math.max(newMaxSpan, c.span)
                    });
                    otherChildren.unshift({
                      id: ids[1],
                      type: 'condition'
                    })
                  } else {
                    otherChildren = [{
                      id: ids[1],
                      type: 'condition'
                    }]
                  }
                  let newTree = {
                    span: newMaxSpan + 1,
                    leaves: [{
                      span: newMaxSpan,
                      children: otherChildren
                    }, {
                      span: 1,
                      children: [{
                        id: ids[2],
                        type: 'condition'
                      }]
                    }]
                  };
                  leaf.children = [
                    ...leaf.children.slice(0, childIndex + 1),
                    {
                      id: ids[0],
                      type: 'open'
                    },
                    newTree,
                    {
                      id: ids[3],
                      type: 'close'
                    }
                  ];
                  if(endNode) {
                    leaf.children.push(endNode);
                  }
                  break;
                case 'add_leaf':
                  leaf.children[childIndex + 1].leaves.push({
                    span: 1,
                    children: [{
                      id: action.id,
                      type: 'condition'
                    }]
                  });
                  leaf.children[childIndex + 1].span++;
                  break;
                case 'remove_tree':
                  leaf.children.splice(childIndex, 3);
                  break;
                case 'remove_leaf':
                  leaves.splice(leafIndex, 1);
                  tree.span--;
              }
            }
          }
          return child;
        });
      }
      return leaf;
    });
    if(source) {
      target = tree;
      target.found = found;
    }
    return target;
  },
  /**
   * 根据id在树内得到路径
   * @param id 节点id
   * @param tree 树
   * @param path 起始路径
   * @return {Array<SectionItem>} SectionItem: {attr: 'leaves' | 'children', index}
   */
  getNodePath: (id, tree, path) => {
    const { leaves = [] } = tree;
    let find = false;
    leaves.map((leaf, leafIndex) => {
      if(!find) {
        path.push({
          attr: 'leaves',
          value: leafIndex,
        });
        leaf.children.map((child, childIndex) => {
          if(!find) {
            path.push({
              attr: 'children',
              value: childIndex
            });
            if(child.leaves && child.leaves.length > 0) {
              let childPath = fastFlowService.getNodePath(id, child, path);
              if(childPath) {
                path = childPath;
                find = true;
              }
            } else if(child.id === id) {
              find = true;
            }
            if(!find)
              path.pop();
          }
        });
        if(!find)
          path.pop();
      }
    });
    return find ? path : false;
  },
  /**
   * 根据id在数内得到以leaf为集合的路径
   * @param id 节点id
   * @param tree 树
   * @param path 起始路径
   * @return {Array<PathItem>} PathItem: {leaf, child}
   */
  getNodePathList: (id, tree, path) => {
    let pathArray = fastFlowService.getNodePath(id, tree, path);
    let treePath = [];
    pathArray.map(section => {
      if(section.attr === 'leaves') {
        treePath.push({
          leaf: section.value
        })
      } else {
        treePath[treePath.length - 1].child = section.value
      }
    });
    return treePath;
  },
  /**
   * 根据传入的路径对应的节点所在的叶子数组，更新整个树的span
   * @param path 起点节点路径
   * @param tree 树
   * @return {*} 调整后的树
   */
  updateSpanByPath: (path, tree) => {
    let targetNodesTree = JSON.parse(JSON.stringify(tree));
    let mirrorLeaf = null;
    let mirrorChild = targetNodesTree;
    let mirrorParent = null;
    path.map(section => {
      mirrorParent = mirrorChild;
      mirrorLeaf = mirrorChild.leaves[section.leaf];
      if(!mirrorLeaf) {
        mirrorChild = mirrorChild.leaves[section.leaf - 1].children[section.child];
      } else {
        mirrorChild = mirrorLeaf.children[section.child];
      }
    });
    if(mirrorLeaf) {
      let maxSpan = 1;
      mirrorLeaf.children.map(child => {
        if(child.span) {
          maxSpan = Math.max(child.span, maxSpan);
        }
      });
      if(mirrorLeaf.span !== maxSpan) {
        mirrorLeaf.span = maxSpan;
      }
    }
    mirrorParent.span = mirrorParent.leaves.reduce((total, leaf) => total + leaf.span, 0);
    let upperPath = path.slice(0, path.length - 1);
    if(upperPath.length === 0)
      return targetNodesTree;
    else
      return fastFlowService.updateSpanByPath(upperPath, targetNodesTree);
  },
  /**
   * 合并叶子，将含有两条分支的树合并成一条分支
   * @param path 分支起点的路径
   * @param tree 树
   */
  mergeTreeByPath: (path, tree) => {
    let targetNodesTree = JSON.parse(JSON.stringify(tree));
    let mirrorLeaf = null;
    let mirrorChild = targetNodesTree;
    let mirrorParentLeaf = null;
    let parentOpenNodeIndex = 0, parentOpenId = 0;
    let leftChildren = null;
    let removeTree = false;
    path.map((section, index) => {
      if(index === path.length - 1) {
        leftChildren = mirrorChild.leaves[1 - section.leaf].children.slice(1);
      }
      if(index === path.length - 2) {
        mirrorParentLeaf = mirrorChild.leaves[section.leaf];
        parentOpenNodeIndex = section.child - 1;
        parentOpenId = mirrorChild.leaves[section.leaf].children[parentOpenNodeIndex].id;
        removeTree =
          mirrorChild.leaves[section.leaf].children.length === 1 &&
          mirrorChild.leaves[1 - section.leaf].children.length === 1
      }
      mirrorLeaf = mirrorChild.leaves[section.leaf];
      mirrorChild = mirrorLeaf.children[section.child];
    });
    if(removeTree) {
      targetNodesTree = fastFlowService.actionById(parentOpenId, targetNodesTree, {
        name: 'remove_tree'
      }, true);
    } else {
      mirrorParentLeaf.children.splice(parentOpenNodeIndex, 3, ...leftChildren);
    }
    let updatePath = path.slice(0, path.length - 1);
    targetNodesTree = fastFlowService.updateSpanByPath(updatePath, targetNodesTree);
    return targetNodesTree;
  }
};

export default fastFlowService
